package service

import (
	"context"
	"encoding/json"
	"github.com/go-pay/gopay"
	wechat2 "github.com/go-pay/gopay/wechat/v3"
	"github.com/jinzhu/copier"
	"go.uber.org/zap"
	v1 "golang-sgg-daijia/api/v1"
	"golang-sgg-daijia/internal/const/enum"
	"golang-sgg-daijia/internal/const/mqconst"
	"golang-sgg-daijia/internal/model"
	"golang-sgg-daijia/internal/repository/mysql"
	"golang-sgg-daijia/internal/repository/rabbitmq"
	"golang-sgg-daijia/internal/repository/wechat"
	"net/http"
	"time"
)

type PaymentService interface {
	CreateWxPayment(ctx context.Context, createWxPaymentForm *v1.CreateWxPaymentForm) (*v1.WxPrepayVo, error)
	QueryPayStatus(ctx context.Context, orderNo string) (bool, error)
	WxNotify(ctx context.Context, request *http.Request) error
	HandleOrder(ctx context.Context, orderNo []byte)
}

func NewPaymentService(
	service *Service,
	driverService DriverService,
	wxPay wechat.WeChatPayClient,
	paymentInfoDao mysql.PaymentInfoDao,
	orderInfoDao mysql.OrderInfoDao,
	customerInfoDao mysql.CustomerInfoDao,
	driverInfoDao mysql.DriverInfoDao,
	orderBillDao mysql.OrderBillDao,
	mqCli rabbitmq.MqProducer,
) PaymentService {
	return &paymentService{
		Service:         service,
		driverService:   driverService,
		wxPay:           wxPay,
		paymentInfoDao:  paymentInfoDao,
		orderInfoDao:    orderInfoDao,
		customerInfoDao: customerInfoDao,
		driverInfoDao:   driverInfoDao,
		orderBillDao:    orderBillDao,
		mqCli:           mqCli,
	}
}

type paymentService struct {
	*Service
	driverService   DriverService
	paymentInfoDao  mysql.PaymentInfoDao
	orderInfoDao    mysql.OrderInfoDao
	customerInfoDao mysql.CustomerInfoDao
	driverInfoDao   mysql.DriverInfoDao
	orderBillDao    mysql.OrderBillDao
	wxPay           wechat.WeChatPayClient
	mqCli           rabbitmq.MqProducer
}

func (s *paymentService) QueryPayStatus(ctx context.Context, orderNo string) (bool, error) {
	queryOrder, err := s.wxPay.QueryPayStatus(ctx, orderNo)
	if err != nil {
		return false, err
	}
	if queryOrder != nil && queryOrder.TradeState == gopay.SUCCESS {
		err = s.handlePayment(ctx, queryOrder)
		if err != nil {
			return false, err
		}
	}
	return true, nil
}

func (s *paymentService) createWxPrePay(ctx context.Context, paymentInfoForm *v1.PaymentInfoForm) (*v1.WxPrepayVo, error) {
	// 1 添加支付记录到支付表里面
	paymentInfo, err := s.paymentInfoDao.FindByOrderNo(ctx, paymentInfoForm.OrderNo)
	if err != nil {
		return nil, err
	}
	if paymentInfo == nil {
		// 判断：如果表存在订单支付记录，不需要添加
		paymentInfoNew := &model.PaymentInfo{}
		copier.Copy(paymentInfoNew, paymentInfoForm).Error()
		paymentInfoNew.PaymentStatus = 0
		err = s.paymentInfoDao.Create(ctx, paymentInfoNew)
		if err != nil {
			return nil, err
		}
	}
	// 调用
	return s.wxPay.Prepay(ctx, int64(paymentInfoForm.Amount), paymentInfoForm.OrderNo,
		paymentInfoForm.Content, paymentInfoForm.CustomerOpenId)
}

func (s *paymentService) CreateWxPayment(ctx context.Context, createWxPaymentForm *v1.CreateWxPaymentForm) (*v1.WxPrepayVo, error) {
	orderPayVo, err := s.orderInfoDao.GetOrderPayVo(ctx, createWxPaymentForm.OrderNo, createWxPaymentForm.CustomerId)
	if err != nil {
		return nil, err
	}
	if orderPayVo.Status != enum.Unpaid.Value() {
		return nil, v1.ErrOrderStatusError
	}
	// 获取司机和顾客openId
	customerInfo, err := s.customerInfoDao.GetById(ctx, createWxPaymentForm.CustomerId)
	if err != nil {
		return nil, err
	}
	customerOpenId := customerInfo.WxOpenID
	driverInfo, err := s.driverInfoDao.GetByID(ctx, orderPayVo.DriverId)
	if err != nil {
		return nil, err
	}
	driverOpenId := driverInfo.WxOpenID

	// 封装到实体类，发起微信支付
	paymentInfoForm := &v1.PaymentInfoForm{
		Amount:         orderPayVo.PayAmount,
		Content:        orderPayVo.Content,
		CustomerOpenId: customerOpenId,
		DriverOpenId:   driverOpenId,
		OrderNo:        orderPayVo.OrderNo,
	}
	return s.createWxPrePay(ctx, paymentInfoForm)

}

func (s *paymentService) WxNotify(ctx context.Context, request *http.Request) error {
	transaction, err := s.wxPay.Notify(ctx, request)
	if err != nil {
		return err
	}

	return s.handlePayment(ctx, transaction)
}

func (s *paymentService) handlePayment(ctx context.Context, trans *wechat2.QueryOrder) error {
	// 1 更新支付记录，状态修改为已支付
	orderNo := trans.OutTradeNo
	paymentInfo, err := s.paymentInfoDao.FindByOrderNo(ctx, orderNo)
	if err != nil {
		return err
	}
	if paymentInfo == nil {
		return v1.ErrOrderPaymentInfoErr
	}
	// 如果已经支付，不需要更新
	if paymentInfo.PaymentStatus == 1 {
		return nil
	}
	paymentInfo.PaymentStatus = 1
	paymentInfo.OrderNo = trans.OutTradeNo
	paymentInfo.TransactionID = trans.TransactionId
	paymentInfo.CallbackTime = time.Now()
	tansStr, err := json.Marshal(trans)
	if err != nil {
		s.logger.WithContext(ctx).Error("paymentService|handlePayment|Marshal", zap.Any("data", map[string]interface{}{
			"trans": trans,
		}), zap.Error(err))
		return err
	}
	paymentInfo.CallbackContent = string(tansStr)
	n, err := s.paymentInfoDao.UpdateByID(ctx, paymentInfo)
	if err != nil {
		return err
	}
	if n == 0 {
		return v1.ErrOrderPaymentInfoErr
	}

	// 2 发送端：发送mq消息，传递订单编号
	err = s.mqCli.SendMessage(ctx, mqconst.ExchangeOrder, mqconst.RoutingPaySuccess, []byte(orderNo))
	if err != nil {
		return err
	}

	// 接收端：获取订单编号，完成后续处理
	return nil
}

func (s *paymentService) HandleOrder(ctx context.Context, orderNo []byte) {
	// 1、更新订单状态：已支付
	orderInfo, err := s.orderInfoDao.GetByOrderNo(ctx, string(orderNo))
	if err != nil {
		return
	}
	if orderInfo == nil || orderInfo.Status == int32(enum.Paid.Value()) {
		return
	}

	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, &model.OrderInfo{
		Status:  int32(enum.Paid.Value()),
		PayTime: time.Now(),
	}, map[string]interface{}{
		"order_no": string(orderNo),
	})
	if err != nil {
		return
	}
	if n == 0 {
		s.logger.WithContext(ctx).Error("orderService|UpdateByEqCondition|更新失败")
	}

	// 2、获取系统奖励，打入到司机账户
	orderBill, err := s.orderBillDao.FindByOrderID(ctx, orderInfo.ID)
	if orderBill != nil && orderBill.RewardFee > 0 {
		transferForm := &v1.TransferForm{
			DriverID:  int(orderInfo.DriverID),
			Amount:    orderBill.RewardFee,
			Content:   "系统奖励",	// 应该改到枚举值，目前只有这一个奖励
			TradeType: 1,
			TradeNo:   string(orderNo),
		}
		_, err = s.driverService.Transfer(ctx, transferForm)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|Transfer|打款失败", zap.Any("data", map[string]interface{}{
				"transferForm": transferForm,
			}), zap.Error(err))
		}
	}
	// 3、todo 其他
}
